package main

import (
	//"crypto/tls"
	//"crypto/x509"
	"crypto/hmac"
	"crypto/sha1"
	"fmt"
	//"strconv"

	"unsafe"

	//"reflect"
	//"encoding/base64"
    //"unicode/utf8"
    //"unicode"
	//"encoding/hex"
	//"io/ioutil"
	"strings"
	//"strconv"
	//"encoding/binary"
	"bytes"
	"time"

	MQTT "github.com/eclipse/paho.mqtt.golang"
	"github.com/xluohome/serial"
)

type AuthInfo struct {
	password, username, mqttClientId string
}


func calculate_sign(clientId, productKey, deviceName, deviceSecret, timeStamp string) AuthInfo {
	var raw_passwd bytes.Buffer
	raw_passwd.WriteString("clientId" + clientId)
	raw_passwd.WriteString("deviceName")
	raw_passwd.WriteString(deviceName)
	raw_passwd.WriteString("productKey")
	raw_passwd.WriteString(productKey)
	raw_passwd.WriteString("timestamp")
	raw_passwd.WriteString(timeStamp)

	// hmac, use sha1
	mac := hmac.New(sha1.New, []byte(deviceSecret))
	mac.Write([]byte(raw_passwd.String()))
	password := fmt.Sprintf("%02x", mac.Sum(nil))
	//password:= mac.Sum(nil)
	//fmt.Println(password)
	username := deviceName + "&" + productKey

	var MQTTClientId bytes.Buffer
	MQTTClientId.WriteString(clientId)
	//hmac, use sha1; securemode=2 means TLS connection
	MQTTClientId.WriteString("|securemode=3,signmethod=hmacsha1,timestamp=")
	MQTTClientId.WriteString(timeStamp)
	MQTTClientId.WriteString(",ext=1|")
	fmt.Println("MQTT ClientID:"+MQTTClientId.String())
	auth := AuthInfo{password: password, username: username, mqttClientId: MQTTClientId.String()}
	return auth
}

/* func NewTLSConfig() *tls.Config {
    // Import trusted certificates from CAfile.pem.
    // Alternatively, manually add CA certificates to default openssl CA bundle.
    certpool := x509.NewCertPool()
    pemCerts, err := ioutil.ReadFile("./x509/root.pem")
    if err != nil {
        //fmt.Println("0. read file error, game over!!")
    }

    certpool.AppendCertsFromPEM(pemCerts)

    // Create tls.Config with desired tls properties
    return &tls.Config{
        // RootCAs = certs used to verify server cert.
        RootCAs: certpool,
        // ClientAuth = whether to request cert from server.
        // Since the server is set up for SSL, this happens
        // anyways.
        ClientAuth: tls.NoClientCert,
        // ClientCAs = certs used to validate client cert.
        ClientCAs: nil,
        // InsecureSkipVerify = verify that cert contents
        // match server. IP matches what is in cert etc.
        InsecureSkipVerify: true,
        // Certificates = list of certs client sends to server.
        // Certificates: []tls.Certificate{cert},
    }
} */

var RcvMsg = func(client MQTT.Client, msg MQTT.Message) {
	inMsg := msg.Payload()
	fmt.Println("Received message on topic:" + msg.Topic())
	fmt.Print("raw data:")

    //得到二进制格式的字符串（bit）
	inStr := (*string)(unsafe.Pointer(&inMsg))
	bitStr:=*inStr
	bitStr=strings.Replace(bitStr,"\x00","",-1)
    fmt.Printf("%v",bitStr)
    // bitStr=strings.Replace(bitStr,"\x00", "", -1)
    // ln:=len(bitStr)
    // //每8个bit转为一个uint值，得到byte[]
    // byteArray:=make([]uint8,ln/8)
    
    // for i:=0;i<ln/8;i++{
    //     sub:=bitStr[i*8:i*8+8]
    //     t,er:=strconv.ParseInt(sub,2,10)
    //     if er==nil{
    //         byteArray[i]=uint8(t)
    //     }
    // }
    // byteMsg:=(*string)(unsafe.Pointer(&byteArray))
    // byteMsgStr:=*byteMsg
    //byteMsgStr=strings.Replace(byteMsgStr,"\x00","",-1)

    //fmt.Print(byteMsgStr)
    //fmt.Print(strconv.ParseInt("01111011",2,10))
    
	//b64Str := base64.StdEncoding.EncodeToString(inMsg)
	//fmt.Println("base64:" + b64Str)

	//l := utf8.RuneCountInString(b64Str)
    //tmp:=make([]string,l)
    //buf:=bytes.NewBuffer([]byte{})

	// for i := 0; i < l; i++ {
    //     st := fmt.Sprintf("%08b", b64Str[i])
    //     fmt.Print(strconv.ParseUint(st,2,32))
    // }
    //fmt.Print(buf.String())
    // for _,t:=range tmp{
    //     fmt.Print(string(t))
    //     fmt.Print(" ")
    // }
}

var clnt MQTT.Client

//IOT平台客户端初始化
func ini() {
	//iotcloud.tencentcloudapi.com
	//iotcloud.tencentdevices.com
	var productKey string = "a1Ss3eJdkTd"
	var deviceName string = "demo-1"
	var deviceSecret string = "22bfxoUbSO6avzWsyIGQn1C6GhEe0jrc"

	var timeStamp string = "1528018257135"
	var clientId string = "go_device_id_0001"
	var subTopic string = "/" + productKey + "/" + deviceName + "/user/get"
	//var subTopic string ="/ext/rrpc/+"+"/"+productKey+"/"+deviceName+"/user/get"
	//var pubTopic string = "/" + productKey + "/" + deviceName + "/user/update";

	var raw_broker bytes.Buffer
	raw_broker.WriteString("tcp://")
	raw_broker.WriteString(productKey)
	raw_broker.WriteString(".iot-as-mqtt.cn-shanghai.aliyuncs.com:1883")
	opts := MQTT.NewClientOptions().AddBroker(raw_broker.String())

	// calculate the login auth info, and set it into the connection options
	auth := calculate_sign(clientId, productKey, deviceName, deviceSecret, timeStamp)
	opts.SetClientID(auth.mqttClientId)
	opts.SetUsername(auth.username)
	opts.SetPassword(auth.password)
	opts.SetKeepAlive(120 * time.Second)
	//opts.SetKeepAlive(1200)一定要写成上面这种，从而转化为time.Duration
	//opts.SetDefaultPublishHandler(f)

	//set the tls configuration
	//tlsconfig := NewTLSConfig()
	//opts.SetTLSConfig(tlsconfig)

	clnt := MQTT.NewClient(opts)
	if rtn := clnt.Connect(); rtn.Wait() && rtn.Error() != nil {
		//连接失败
		panic(rtn.Error())
	}
	fmt.Print("Connect aliyun IoT Cloud Sucess\n")
	if token := clnt.Subscribe(subTopic, 0, RcvMsg); token.Wait() && token.Error() != nil {
        fmt.Println(token.Error())
    }
    fmt.Print("Subscribe topic " + subTopic + " success\n");
}

func close() {
	if clnt == nil {
		return
	}
	clnt.Disconnect(250)
}

func pub(topic string, msg string) {
	clnt.Publish(topic, 1, false, msg)
}

func iniSerial() {
	c := &serial.Config{Name: "COM9", Baud: 9600}
	s, err := serial.OpenPort(c)
	if err != nil {

	} else {
		fmt.Println(s)
	}
	//boot=0xcc,0x00,0x01,0x03,0x0f,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0
	cmd := []byte{0xcc, 0x00, 0x01, 0x03, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0}
	s.Write(cmd)
}

func main() {
	//var p int
	//fmt.Println("请输入串口号:")
    //fmt.Scanln(&p)
    
	go ini()
	select {}
}
